package serveryt

import (
	"google.golang.org/grpc"
	"google.golang.org/grpc/health"
)

/**
ServerYT 将所有grpc服务的功能通过流水线方法暴露出来。使用者可以再往上封装，比如加上配置，加上自动注册等功能。但是底层还是对流水线方法的灵活调用
*/
import (
	"errors"
	healthpb "google.golang.org/grpc/health/grpc_health_v1"
	"net"
	"strings"
)

type (
	ServerYT struct {
		options            []grpc.ServerOption
		streamInterceptors []grpc.StreamServerInterceptor
		unaryInterceptors  []grpc.UnaryServerInterceptor
		h                  *health.Server
		addr               string
		s                  *grpc.Server
	}
)

func NewServerYT() *ServerYT {
	return &ServerYT{}
}

func (s *ServerYT) WithGrpcOption(options ...grpc.ServerOption) {
	s.options = append(s.options, options...)
}
func (s *ServerYT) WithStreamInterceptors(interceptors ...grpc.StreamServerInterceptor) {
	s.streamInterceptors = append(s.streamInterceptors, interceptors...)
}

func (s *ServerYT) WithUnaryInterceptors(interceptors ...grpc.UnaryServerInterceptor) {
	s.unaryInterceptors = append(s.unaryInterceptors, interceptors...)
}
func (s *ServerYT) EnableHealthCheck() {
	s.h = health.NewServer()
}
func (s *ServerYT) EnableHealthCheckName(name string) {
	if s.h != nil {
		s.h.SetServingStatus(name, serverStatusToPb(SERVING))
	}
}
func (s *ServerYT) SetHealthCheckStatus(status ServerStatus) {
	if s.h != nil {
		s.h.SetServingStatus("", serverStatusToPb(status))
	}
}
func (s *ServerYT) SetHealthCheckStatusName(name string, status ServerStatus) {
	if s.h != nil {
		s.h.SetServingStatus(name, serverStatusToPb(status))
	}
}

type ServerStatus uint8

const (
	SERVICE_UNKNOWN ServerStatus = iota
	NOT_SERVING
	SERVING
)

func serverStatusToPb(status ServerStatus) healthpb.HealthCheckResponse_ServingStatus {
	switch status {
	case NOT_SERVING:
		return healthpb.HealthCheckResponse_NOT_SERVING
	case SERVICE_UNKNOWN:
		return healthpb.HealthCheckResponse_SERVICE_UNKNOWN
	case SERVING:
		return healthpb.HealthCheckResponse_SERVING
	}
	return healthpb.HealthCheckResponse_SERVICE_UNKNOWN
}

type RegisterFn func(*grpc.Server)

func (s *ServerYT) ServeAt(addr string) *ServerYT {
	s.addr = addr
	return s
}

// Prepare 做一些基础检查并初始化一些配置，grpc服务
func (s *ServerYT) prepare() error {
	if err := validate(s); nil != err {
		return err
	}
	if len(s.unaryInterceptors) > 0 {
		unaryInterceptorOption := grpc.ChainUnaryInterceptor(s.unaryInterceptors...)
		s.options = append(s.options, unaryInterceptorOption)
	}
	if len(s.unaryInterceptors) > 0 {
		streamInterceptorOption := grpc.ChainStreamInterceptor(s.streamInterceptors...)
		s.options = append(s.options, streamInterceptorOption)
	}

	server := grpc.NewServer(s.options...)
	s.s = server
	// register the health check service
	if s.h != nil {
		healthpb.RegisterHealthServer(server, s.h)
		s.h.Resume()
	}
	return nil
}

// Start 必须先调用prepare
// 模板设计模式
func (s *ServerYT) Start(fn RegisterFn) error {
	if err := s.prepare(); nil != err {
		return err
	}
	if nil != s.s && nil != fn {
		fn(s.s)
	}
	lis, err := net.Listen("tcp", s.addr)
	if err != nil {
		return err
	}
	return s.s.Serve(lis)
}
func (s *ServerYT) StartAsync(fn RegisterFn) (<-chan error, error) {
	if err := s.prepare(); nil != err {
		return nil, err
	}
	if nil != s.s && nil != fn {
		fn(s.s)
	}
	lis, err := net.Listen("tcp", s.addr)
	if err != nil {
		return nil, err
	}
	return startAsync(lis, s), nil
}
func startAsync(lis net.Listener, s *ServerYT) <-chan error {
	ch := make(chan error)
	go func() {
		defer close(ch)
		if err := s.s.Serve(lis); nil != err {
			ch <- err
		}
	}()
	return ch
}

func (s *ServerYT) Stop() error {
	if nil != s.s {
		if s.h != nil {
			s.h.Shutdown()
		}
		s.s.GracefulStop()
	}
	return nil
}
func (s *ServerYT) StopNow() error {
	if nil != s.s {
		if s.h != nil {
			s.h.Shutdown()
		}
		s.s.Stop()
	}
	return nil
}
func validate(s *ServerYT) error {
	if len(strings.TrimSpace(s.addr)) == 0 {
		return errors.New("server address is empty")
	}
	return nil
}
